#include "Detector.h"
#include "../AiBox.h"
#include "../config/SystemConfig.h"
#include "../service/DeviceService.h"
#include "../service/EventRecordService.h"
#include "../util/DateTime.h"
#include "Broadcast.h"

Detector::Detector(DeviceInfo device)
	: m_run(true)
	, m_device(device)
	, m_frame_count(0)
{
}

Detector::~Detector()
{
}

void Detector::Start()
{
	// 创建视频窗口
#ifdef WIN32
	//cv::namedWindow(this->m_device.GetId());
#endif
	LOG_INFO("start device {} detect.", m_device.GetId());

	// 启动视频检测
	m_run = true;
	// 启动录像管理
	m_record_manager.Start();
	// 启动视频采集
	m_thread = std::thread([&]() {
		// 创建视频流
		m_stream = std::make_shared<Stream>(m_device.GetId());
		// 启动时间
		m_last_update = DateTime::CurrrentTimePoint();
		// 回调函数
		auto func = std::bind(&Detector::VideoBack, this,
			std::placeholders::_1,
			std::placeholders::_2,
			std::placeholders::_3,
			std::placeholders::_4,
			std::placeholders::_5,
			nullptr);
		// 设置回调
		m_stream->SetCallback(func, this);
		// 启动视频
		m_stream->Start(m_device.GetRtspUrl());
		// 检测线程
		while (m_run)
		{
			// 缓存项目
			CacheItem item;
			{
				// 资源队列
				std::unique_lock<std::mutex> _guard(m_cache_lock);
				if (m_cache_queue.size() > 0)
				{
					item = m_cache_queue.front();
					m_cache_queue.pop();
				}
			}

			// 有识别项
			if (item)
			{
				// 显示报警视频
				if (SystemConfig::get_mutable_instance().GetLocalConf().GetPreview())
				{
					try
					{
						// 图片缩放n倍后显示
						float scale = 1.0;
						cv::resize(item->img, item->img, cv::Size(item->img.cols * scale, item->img.rows * scale));
						cv::imshow(item->device, item->img);
						// wait(0)会无线等待输入
						cv::waitKey(1);
					}
					catch (cv::Exception& e)
					{
						// ssh无法预览则不显示
						SystemConfig::get_mutable_instance().GetLocalConf().SetPreview(false);
					}
				}

				// 统计报警信息
				int alarm_count = 0;
				std::set<std::string> alarmSets, infoSets;
				for (size_t index = 0; index < item->results.size(); ++index)
				{
					OCR_RESULT& result = item->results[index];
					if (alarmSets.find(result.alarmType) == alarmSets.end())
					{
						alarmSets.insert(result.alarmType);
						infoSets.insert(result.alarmInfo);
					}
					alarm_count += result.count;
				}

				// 去除尾部逗号
				std::string alarmType = TextUtil::get_mutable_instance().JoinString(alarmSets);
				std::string alarmInfo = TextUtil::get_mutable_instance().JoinString(infoSets);

				// 预警静默周期
				if (m_last_type != alarmType || DateTime::Duration(m_last_time) > m_device.GetSilence())
				{
					LOG_INFO("detect device alarm, type {}", alarmType);

					// 上次预警信息
					m_last_time = DateTime::CurrrentTimePoint();
					m_last_type = alarmType;

					// 生成报警图像
					std::string alarmId = TextUtil::get_mutable_instance().GetGuid();
					std::string imagePath = SystemHelper::get_mutable_instance().GetDeviceImagePath(m_device.GetId(), alarmId);
					cv::imwrite(imagePath.c_str(), item->img);

					// 生成报警记录
					AlarmInfo alarm;
					alarm.SetId(alarmId);
					alarm.SetDeviceId(m_device.GetId());
					alarm.SetDeviceName(m_device.GetName());
					alarm.SetAlarmTime(DateTime::Now());
					alarm.SetUpload(0);
					alarm.SetImagePath(imagePath);
					alarm.SetImageUrl(SystemConfig::get_mutable_instance().GetLocalConf().GetAlarmUrl(alarm.GetId()));
					alarm.SetUploadCount(0);
					alarm.SetCount(alarm_count);
					alarm.SetUploadRecord(m_record_manager.HasManualRecord());
					alarm.SetAlarmType(alarmType);
					alarm.SetAlarmInfo(alarmInfo);

					// 保存报警记录
					AlarmService alarmService;
					if (alarmService.Add(alarm))
					{
						// 添加广播记录
						ALARM_INFO info;
						info.id = alarmInfo;
						info.alarmTime = alarm.GetAlarmTime();
						info.alarmInfo = alarm.GetAlarmInfo();
						info.alarmType = alarm.GetAlarmType();
						info.deviceId = alarm.GetDeviceId();
						info.deviceName = alarm.GetDeviceName();
						info.imageUrl = alarm.GetImageUrl();
						Broadcast::get_mutable_instance().Push(info);

						// 启用报警录像
						if (SystemConfig::get_mutable_instance().GetBoxConf().GetAlarmRecord())
						{
							// 报警录像回调
							auto call = std::bind(&Detector::AlarmRecordCB, this,
								std::placeholders::_1,
								std::placeholders::_2,
								std::placeholders::_3,
								std::placeholders::_4);

							// 启动报警录像
							LOG_INFO("start alarm record for device {}", alarm.GetDeviceId());
							m_record_manager.StartAlarmRecord(alarm.GetId(), alarm.GetDeviceId(), 5, call);
						}
					}
					else
					{
						FileUtil::RemoveFile(imagePath);
					}
				}
			}

			// 及时退出
			int millisec = m_device.GetFrameGap() * 1000 / 25;
			if (millisec > 1000)
			{
				bool exit = false;
				int gap = (millisec + 999) / 1000;
				for (int index = 0; index < gap; ++index)
				{
					if (!m_run)
					{
						exit = true;
						break;
					}
					std::this_thread::sleep_for(std::chrono::seconds(1));
				}
				if (exit)
				{
					break;
				}
			}
			else
			{
				std::this_thread::sleep_for(std::chrono::milliseconds(millisec));
			}
		}
		// 停止视频
		m_stream->Close();
	});
}

void Detector::Stop()
{
	// 停止检测线程
	LOG_INFO("stop device {} detect.", m_device.GetId());
	m_run = false;
	if (m_thread.joinable())
	{
		m_thread.join();
	}

	// 关闭视频窗口
	if (SystemConfig::get_mutable_instance().GetLocalConf().GetPreview()) {
		try
		{
			cv::destroyWindow(m_device.GetId());
		}
		catch (cv::Exception& e)
		{

		}
	}

	// 停止录像管理
	m_record_manager.Stop();
}

bool Detector::Change(DeviceInfo info)
{
	return 0 != DateTime::Diff(m_device.GetUpdateTime(), info.GetUpdateTime());
}

bool Detector::StartRecord()
{
	// 录像信息
	std::string id = TextUtil::get_mutable_instance().GetGuid();

	// 报警录像回调
	auto call = std::bind(&Detector::ManualRecordCB, this,
		std::placeholders::_1,
		std::placeholders::_2);

	// 启动手动录像
	bool success = m_record_manager.StartManualRecord(id, m_device.GetId(), m_device.GetName(), 30, call, true);
	if (success)
	{
		m_record_id = id;
	}
	return success;
}

void Detector::StopRecord()
{
	m_record_manager.StopManualRecord(m_record_id);
}

bool Detector::StartCapture()
{
	return m_record_manager.StartManualCapture(m_record_id);
}

int Detector::AlarmBack(std::string device, std::vector<OCR_RESULT>& results, cv::Mat& frame, void*)
{
	// 获取监视器
	AiMonitorPtr monitor = Box::get_mutable_instance().GetAiMonitor();
	if (!monitor)
	{
		return 0;
	}

	// 检测器存在
	DetectorPtr detector = monitor->Find(device);
	if (detector)
	{
		// 新建缓存
		CacheItem item = std::make_shared<OCR_CACHE>();
		item->device = device;
		item->img = frame.clone();
		item->results = results;

		// 资源队列
		std::unique_lock<std::mutex> _guard(m_cache_lock);

		// 加入分析队列
		m_cache_queue.push(item);
	}
	return 0;
}

int Detector::VideoBack(std::string device, int width, int height, int fps, cv::Mat& frame, void*)
{
	// 录像初始化
	m_record_manager.Init(fps, width, height);
	// 输入图像帧
	m_record_manager.InputFrame(frame);

	// 更新设备图片
	if (DateTime::Duration(m_last_update) > 20)
	{
		// 保存设备图片
		m_last_update = DateTime::CurrrentTimePoint();
		std::string image_id = m_device.GetId();
		std::string imagePath = SystemHelper::get_mutable_instance().GetDeviceImagePath(m_device.GetId(), image_id);
		if (cv::imwrite(imagePath.c_str(), frame))
		{
			// 移除老的图片
			std::string last_image = m_device.GetLastImage();
			FileUtil::RemoveFile(last_image);

			// 保存新的图片
			cv::imwrite(imagePath, frame);

			// 更新内存图片
			m_device.SetDetectTime(DateTime::Now());
			m_device.SetLastImage(imagePath);
			m_device.SetImageUrl(SystemConfig::get_mutable_instance().GetLocalConf().GetImageUrl(image_id));

			// 更新数据库图
			DeviceService deviceService;
			deviceService.UpdateImage(m_device);
		}
	}

	// 获取引擎
	EnginePtr engine = Box::get_mutable_instance().GetEngine();
	if (!engine)
	{
		return 0;
	}

	// 分析采样率
	m_frame_count++;
	if (m_frame_count != m_device.GetFrameGap())
	{
		return 0;
	}
	m_frame_count = 0;

	// 报警回调
	auto call = std::bind(&Detector::AlarmBack, this,
		std::placeholders::_1,
		std::placeholders::_2,
		std::placeholders::_3,
		std::placeholders::_4);

	// 识别项目
	OcrItemPtr item = std::make_shared<OcrItem>();
	item->SetImage(frame);
	item->SetDevice(device);
	item->SetCallback(call);
	item->SetContext(this);

	// 模型推理
	engine->Push(item);
	return 0;
}

// 报警编码 设备编码 录像路径 录像时长
int Detector::AlarmRecordCB(std::string alarmId, std::string deviceId, std::string path, int duration)
{
	// 报警录像记录
	AlarmInfo record;
	record.SetId(alarmId);
	record.SetRecordPath(path);
	record.SetRecordUrl(SystemConfig::get_mutable_instance().GetLocalConf().GetRecordUrl(alarmId));

	// 更新报警记录
	AlarmService alarmService;
	alarmService.Update(record);
	LOG_INFO("alarm record finish for device {}", deviceId);
	return 0;
}

// 结束状态 事件编码 是否图片 录像编码 设备编码 设备名称 录像路径 开始时间 结束时间 录像时长
int Detector::ManualRecordCB(bool finish, RECORD_INFO recordInfo)
{
	EventRecordInfo record;
	record.SetId(recordInfo.recordId);
	record.SetEventId(recordInfo.eventId);
	record.SetDeviceId(recordInfo.deviceId);
	record.SetDeviceName(recordInfo.deviceName);
	record.SetCreateTime(DateTime::Now());
	record.SetDuration(recordInfo.duration);
	record.SetEventType(0);
	record.SetImage(recordInfo.image);
	record.SetName(FileUtil::GetFileName(recordInfo.recordPath));
	record.SetPath(recordInfo.recordPath);
	record.SetSize(FileUtil::GetFileSize(recordInfo.recordPath));
	record.SetStartTime(recordInfo.startTime);
	record.SetStopTime(recordInfo.stopTime);
	record.SetUpload(0);
	record.SetUrl(SystemConfig::get_mutable_instance().GetLocalConf().GetManualUrl(recordInfo.recordId));

	EventRecordService recordService;
	recordService.Add(record);
	return 0;
}